home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 July / EnigmA AMIGA RUN 20 (1997)(G.R. Edizioni)(IT)[!][issue 1997-07 & 08][EAR-CD IV].iso / lightwave / tips / lwdoc.txt < prev    next >
Internet Message Format  |  1996-12-09  |  50KB

  1. From shf@netcom.com
  2. Date: Fri, 6 Jan 1995 19:13:05 -0800
  3. From: Stuart Ferguson <shf@netcom.com>
  4. Reply to: lwplugin-l@netcom.com
  5. To: lwplugin-l@netcom.com
  6. Subject: LWSDK: flat text docs
  7.  
  8.  
  9. LightWave Plug-in Architecture -- Stuart Ferguson 1/6/95
  10.  
  11. 1  Plug-In Interface
  12.     1.1  Server Identification
  13.     1.2  Server Activation Function
  14.     1.3  The Global Function
  15.     1.4  Plug-in and Built-in Servers
  16.  
  17. 2  Server Interface
  18.     2.1  Plug-in Initialization and Cleanup
  19.        (1)  Startup usage
  20.        (2)  Shutdown usage
  21.     2.2  Activation Function
  22.        (3)  Activation function args
  23.        (4)  ActivateFunc type
  24.     2.3  Global Function
  25.        (5)  GlobalFunc types
  26.        (6)  GlobalFunc types
  27.     2.4  The Global Server Class
  28.        (7)  Global activation data
  29.     2.5  External Function Entry Points
  30.        (8)  XCALL Definitions
  31.     2.6  Single-Service Plug-ins
  32.        (9)  Activate usage
  33.     2.7  Multiple-Service Plug-ins
  34.        (10)  ServerRecord type
  35.  
  36. 3  Example Plug-in Service
  37.     3.1  String Transform Class
  38.        (11)  Test types
  39.        (12)  Test types
  40.     3.2  String Transform Functions
  41.        (13)  String transform arguments
  42.     3.2.1  Length Operation
  43.        (14)  Length function body
  44.     3.2.2  Reverse Operation
  45.        (15)  Reverse function body
  46.     3.2.3  Capitalization Operation
  47.        (16)  Capitalize function body
  48.     3.2.4  Double Operation
  49.        (17)  Double function body
  50.     3.3  Implementing Servers
  51.     3.3.1  Single-Service Plug-in -- Reverse
  52.        (18)  Test Reverse plug-in program
  53.     3.3.2  Multiple-Service Plug-in -- Caps & Double
  54.        (19)  Test Caps and Double plug-in program
  55.     3.3.3  Built-in Server -- Length
  56.        (20)  Test host utilities
  57.  
  58. 4  Creating a Plug-in
  59.     4.1  Amiga -- SAS/C Compiler
  60.        (21)  Makefile examples
  61.     4.2  Amiga -- Manx Compiler
  62.        (22)  Makefile examples
  63.     4.3  Microsoft's Windows
  64.        (23)  Makefile examples
  65.     4.4  SGI Unix
  66.        (24)  Makefile examples
  67.     4.5  Linking with LightWave
  68.        (25)  Config file examples
  69.        (26)  Config file examples
  70.  
  71.  
  72. 1    Plug-In Interface
  73.  
  74. There are two parts to the system-generic plug-in interface: the host
  75. side and the server side.  The host is the application program which
  76. wants to load external code modules to perform some generic type of
  77. operation.  Servers are imported routines (which can be loaded
  78. plug-ins or internal built-ins) which implement a specific instance of
  79. a generic type of service.
  80.  
  81. The host interface provides facilities to create server classes,
  82. register plug-in modules, and perform lazy loading and activation of
  83. registered servers.  There is a fairly elaborate name and type mapping
  84. scheme which allows a great deal of flexibility in how modules are
  85. used, but which still provides a fairly simple interface for those who
  86. do not need the full facility.
  87.  
  88. The server interface provides an easy method to write programs that
  89. will operate as plug-ins.  Different classes of plug-in services will
  90. require different host interfaces, but the loading and initialization
  91. part of the server interface is standard and works with the host
  92. portion of the system.
  93.  
  94.  
  95. 1.1    Server Identification
  96.  
  97. The plug-in interface is designed to allow the host to have any number
  98. of servers loaded to perform as many different functions as the host
  99. wants to define.  The servers in the system are referenced by a
  100. combination of class and name.
  101.  
  102. A Server Class is a string which determines the type of service which
  103. the server can perform.  This might be strings like "TEXTURE" or
  104. "FileRequester".  Many servers can have the same class, and all
  105. servers of the same class have the same host interface.
  106.  
  107. A Server Name is a string which refers to a specific server within a
  108. given class.  This might be something like "FractalNoise3D" or
  109. "Default".  The name must be unique among the servers of the same
  110. class.
  111.  
  112. The names for class and server identification should be byte strings
  113. containing characters only the the ASCII range 33-127.  By convention
  114. these string contain no spaces and no characters outside 7-bit ASCII.
  115. Case is significant in distinguishing different classes and servers
  116. within classes.
  117.  
  118.  
  119. 1.2    Server Activation Function
  120.  
  121. Every server has a single `activation' function.  This is the function
  122. which the host calls to access the service provided by the server.
  123. For some servers this one function will perform the whole action and
  124. for others this will only be a prelude to a sequence of actions.
  125. Servers which must remain loaded after they return from their
  126. activation function must be locked by the host while there are actions
  127. pending or they may be unloaded.
  128.  
  129.  
  130. 1.3    The Global Function
  131.  
  132. The activation function for every server is called with a `global'
  133. function pointer which provides access to the internal global state of
  134. the host system.  The server calls the function with a string
  135. identifing the global data requested and a flag for how it will be
  136. used.  The host can service this request, or the request can be passed
  137. on to global plug-in servers.
  138.  
  139.  
  140. 1.4    Plug-in and Built-in Servers
  141.  
  142. Servers can be either plug-in or built-in.  A plug-in server is
  143. implemented as a file containing code that can be loaded and unloaded
  144. as needed.  A built-in server is implemented as a callback within the
  145. host itself.  Having both allows the host to provide a standard set of
  146. servers which it handles the same way it handles plug-in servers,
  147. without having to unbundle their functionality in a way that can be
  148. replaced or used by other programs.
  149.  
  150.  
  151. 2    Server Interface
  152.  
  153. A plug-in server is written like any ordinary C program, but instead
  154. of a single "main()" entry point, a server has a different primary
  155. entry point and several possible additional entry points.  The server
  156. is linked with initializaion code (different from the normal shell or
  157. Workbench init) which places these interfaces where the host can
  158. access them.
  159.  
  160. There are two main types of plug-in modules: those providing a single
  161. server and those providing multiple servers.  It is simple to have one
  162. server per module, but it can be more efficient and useful to define
  163. many servers with a single code file.
  164.  
  165. All servers require an activation function, and all plug-ins have the
  166. option of providing initialization and cleanup functions.  The header
  167. for server types is `splug.h'.
  168.  
  169.  
  170. 2.1    Plug-in Initialization and Cleanup
  171.  
  172. In both the single and multiple versions of the plug-in module, there
  173. are optional entry points which allow the module to initialize itself
  174. when it is first loaded and to clean itself up before being unloaded.
  175. If the plug-in code does not contain functions with these names, no
  176. attempt will be made to call them.
  177.  
  178. The Startup function, if present, will be called when the plug-in is
  179. first loaded into the host system.  The return value is global data
  180. for the server which is passed to the Activate and Shutdown entry
  181. points as ` serverData'.  A zero return value (null pointer)
  182. indicates failure, so even a plug-in with no data should return
  183. something.
  184.  
  185.     (1)  Startup usage
  186.  
  187.         void *
  188.     Startup (void)
  189.  
  190. If provided, the server's Shutdown function is called just before the
  191. server module is unloaded from the host.  Any allocated server data
  192. should be freed at this point.  Note that even though it is an error,
  193. this function may be called even when the server is locked, so correct
  194. cleanup should be done in this case as well.
  195.  
  196.     (2)  Shutdown usage
  197.  
  198.         void
  199.     Shutdown (
  200.         void                    *serverData)
  201.  
  202.  
  203. 2.2    Activation Function
  204.  
  205. All servers have a single activation function which is the entry point
  206. for the host to get access to the service provided by the server.  The
  207. activation function gets passed the version number for the service
  208. implementation, the `global' pointer to access global host data,
  209. class-specific `local' data, and private data maintained by the
  210. plug-in.  The version number is application-defined, but typically it
  211. represents the revision of the interface that the host expects the
  212. server to use.  Typically a server will not attempt to operate if the
  213. version number is greater than it expects.  The ` serverData' is
  214. returned by the Startup entry point in a plug-in.  The global function
  215. can be called to get global data from the host enviroment, and the
  216. contents of the `local' pointer are defined by the type of service.
  217.  
  218.     (3)  Activation function args
  219.  
  220.     long             version,
  221.     GlobalFunc      *global,
  222.     void            *local,
  223.     void            *serverData
  224.  
  225. The activation function returns an error code if the attempt to call
  226. failed because of some clash between the server and the host
  227. environment.  If the server was able to process the request, even it
  228. failed to complete it, it should return AFUNC_OK.  If the version
  229. number is not a value which the server can explicitly handle it should
  230. return AFUNC_BADVERSION.  If there is some global data the server
  231. cannot get which it requires it should return AFUNC_BADGLOBAL.  Severe
  232. problems with the contents of the local data, such as some necessary
  233. pointer in the local data being null, may be reported by returning
  234. AFUNC_BADLOCAL.  Any other errors from the server (running out of
  235. memory, bad filenames, user aborts, etc.) must be provided for by the
  236. specific plug-in protocol.
  237.  
  238.     (4)  ActivateFunc type
  239.  
  240.     typedef int     ActivateFunc (<Activation function args>);
  241.  
  242.     #define AFUNC_OK                0
  243.     #define AFUNC_BADVERSION        1
  244.     #define AFUNC_BADGLOBAL         2
  245.     #define AFUNC_BADLOCAL          3
  246.  
  247.  
  248. 2.3    Global Function
  249.  
  250. The global function passed by the host to the server is a special
  251. function which returns the pointer to some global data given by a
  252. string ID.  These data blocks will often contain function pointers,
  253. but can be anything.
  254.  
  255.     (5)  GlobalFunc types
  256.  
  257.     typedef void *          GlobalFunc (const char *, int);
  258.     . . .
  259.  
  260. When a server calls the global function, it passes a string which
  261. identifies the global data required and a code for the way the data
  262. will be used.  If the data pointer is not available, null is returned.
  263. The ID's that will be recognized depends on the host, on the available
  264. global plug-ins and perhaps on the server class.
  265.  
  266. The use code depends on how the result of the call will be used.  If
  267. the returned pointer will only be used for the course of the
  268. activation function itself, the TRANSIENT code should be used.  If the
  269. data will be used after the activation function returns, such as in a
  270. server that requires locking, the ACQUIRE code should be used.  In
  271. this case there must be a matching RELEASE call made when the data
  272. pointer is no longer required.  RELEASE calls need only be made for
  273. ACQUIRE calls which returned a non-null pointer.  The return value
  274. from a release mode global data call is undefined.
  275.  
  276.     (6)  GlobalFunc types
  277.     . . .
  278.     #define GFUSE_TRANSIENT         0
  279.     #define GFUSE_ACQUIRE           1
  280.     #define GFUSE_RELEASE           2
  281.  
  282.  
  283. 2.4    The Global Server Class
  284.  
  285. The server class given by the name "Global" is special in that it
  286. allows multiple plug-in servers to share common data or routines.  In
  287. fact, the members of the Gobal class are extensions to the set of ID
  288. strings that can be passed to the "global" function.
  289.  
  290. When a server calls the global function with an ID string, the host
  291. can service the request itself or has the option of pass unrecognized
  292. ID's to Global class servers of the same name.  For example, if the ID
  293. is "Mambo Functions," the host may recognize this itself and return a
  294. pointer value.  If it does not recognize it, it may attempt to
  295. activate a server of class "Global" with name "Mambo Functions." If
  296. such a server exists, it may be locked or unlocked, depending on the
  297. use type of the global request, and it will be called to get the value
  298. of the global pointer for the orignal requester.
  299.  
  300. The activation function of a Global server is called with a
  301. GlobalService structure which will be initialized with the ID string
  302. for the request.  The server must fill in the data pointer with a
  303. value which will be returned to the client, which may be null if the
  304. server wishes to deny the request.  The string is passed as data so
  305. that the same activation function may be used for multiple servers.
  306.  
  307.     (7)  Global activation data
  308.  
  309.     typedef struct st_GlobalService {
  310.         const char      *id;
  311.         void            *data;
  312.     } GlobalService;
  313.  
  314.  
  315. 2.5    External Function Entry Points
  316.  
  317. Functions in the plug-in get called directly by the host, and this is
  318. a funky thing in some systems since they are different environments.
  319. The XCALL_ and XCALL_INIT macros take care of everything for all
  320. different systems and compilers, so these can be used to make
  321. multi-platform servers from a single source code.
  322.  
  323. XCALL_ is used on the return type, e.g.  XCALL_(int) for an external
  324. entry point returning an int.  XCALL_INIT is used as the first
  325. statement of the function.  Both must be used for full compatibility,
  326. but XCALL_INIT is only non-null for Manx small-code modules.
  327.  
  328.     (8)  XCALL Definitions
  329.  
  330.     <XCALL_ and XCALL_INIT system-specific definition>
  331.  
  332. The activation function as well as any function pointers returned from
  333. the activation function need the XCALL treatment.  Startup and
  334. Shutdown do not.
  335.  
  336.  
  337. 2.6    Single-Service Plug-ins
  338.  
  339. A single-service plug-in is a C program with an entry point for the
  340. activation callback and global symbols for the class and name of the
  341. server.  There are also optional entry points for initialization and
  342. cleanup.
  343.  
  344. This plug-in must contain a global character string with the name
  345. `ServerClass'.  This string defines the class of this server and the
  346. server will not be loaded if this string does not match the service
  347. type string requested by the host.
  348.  
  349. It must also contain a global character string called `ServerName'
  350. which holds the name for this specific server.
  351.  
  352. The activation function must be called Activate, which takes the
  353. arguments as described above.
  354.  
  355.     (9)  Activate usage
  356.  
  357.         XCALL_(int)
  358.     Activate (<Activation function args>)
  359.  
  360.  
  361. 2.7    Multiple-Service Plug-ins
  362.  
  363. A multiple-service plug-in is a C program which defines multiple
  364. servers through a standard set of global symbols.  In particular, a
  365. multiple server module must contain a global array with the name
  366. `ServerDesc' composed of elements of the ServerRecord type.  The last
  367. record in the array must have a null class name pointer.
  368.  
  369.     (10)  ServerRecord type
  370.  
  371.     typedef struct st_ServerRecord {
  372.         const char      *class;
  373.         const char      *name;
  374.         ActivateFunc    *activate;
  375.     } ServerRecord;
  376.  
  377. The plug-in module may also have Startup and Shutdown entry points,
  378. and all the activate functions in the plug-in will get the same
  379. serverData as returned from the Startup function.  The assumption is
  380. that the servers all share a module for some logical reason, so the
  381. sharing of global data is not unreasonable.
  382.  
  383.  
  384. 3    Example Plug-in Service
  385.  
  386. This describes a hypothetical server class and creates some samples of
  387. plug-in modules using it.  This serves as a testbed for third parties
  388. to create test plug-ins, so it should have some general capability.
  389.  
  390.  
  391. 3.1    String Transform Class
  392.  
  393. This server class will perform manipulations on character strings,
  394. like reverse them, capitalize them, etc.  This class will be
  395. "StringXfrm".
  396.  
  397. A new server class is completely defined by the semantics of the
  398. activation function for the class.  The activation function takes a
  399. pointer argument from the host, `local' which is a reference to data
  400. for the particular service the host needs performed.  It also gets a
  401. `global' function pointer which will return global data as needed by
  402. the server.
  403.  
  404. The `local' pointer will point to a StringLocal structure which holds
  405. the data for the current operation.  This is a null-terminated string
  406. and the length of the string buffer, plus a temporary scratch buffer
  407. and its length.  The server will overwrite `buf' with the result, and
  408. will set the `overflow' status flag if the buffers were too short.
  409.  
  410.     (11)  Test types
  411.  
  412.     typedef struct st_StringLocal {
  413.         char            *buf;
  414.         char            *tmpBuf;
  415.         int              len, tmpLen;
  416.         int              overflow;
  417.     } StringLocal;
  418.     . . .
  419.  
  420. For the string transform class of server, the global function can
  421. return a `progress' function which can be called by the server to give
  422. the user feedback about its progress.  This is returned using a string
  423. ID of "Progress Function."
  424.  
  425.     (12)  Test types
  426.     . . .
  427.     typedef void            StringProgress (void);
  428.  
  429. We'll stick these definitions into the `t_plug.h' header file for test
  430. modules to use.
  431.  
  432.  
  433. 3.2    String Transform Functions
  434.  
  435. The functions to do string transformations are all the same.  They all
  436. get the same arguments as defined by the format of the activation
  437. function.  The local pointer is specific to the string transform
  438. class.  There is no ` serverData' for any of the transforms since
  439. there is no Startup function.
  440.  
  441.     (13)  String transform arguments
  442.  
  443.     long                     version,
  444.     GlobalFunc              *global,
  445.     StringLocal             *local,
  446.     void                    *serverData
  447.  
  448. String activation functions may return with an error code if the
  449. version number is wrong or if the global progress function is not
  450. available.
  451.  
  452.     3.2.1    Length Operation
  453.  
  454.     The length operation gets the length of the string and prints that
  455.     as a number into the string buffer.
  456.  
  457.     (14)  Length function body
  458.  
  459.         {
  460.             if (version != 1)
  461.                 return AFUNC_BADVERSION;
  462.  
  463.             if (local->len < 10)
  464.                 local->overflow = 1;
  465.             else
  466.                 sprintf (local->buf, "%ld", strlen (local->buf));
  467.  
  468.             return AFUNC_OK;
  469.         }
  470.  
  471.     3.2.2    Reverse Operation
  472.  
  473.     The reverse operation copies the characters from the main buffer
  474.     to the temp buffer in reverse order and then copies them back.
  475.     This could use a swap operation to reverse them in place, but this
  476.     method demonstrates using the temp buffer and returning an
  477.     overflow if the temp buffer is too small.  This also calls the
  478.     progress function as it swaps.
  479.  
  480.     (15)  Reverse function body
  481.  
  482.         {
  483.             StringProgress          *progress;
  484.             int                      len, i;
  485.  
  486.             if (version != 1)
  487.                 return AFUNC_BADVERSION;
  488.  
  489.             progress = (*global) ("Progress Function",
  490.                GFUSE_TRANSIENT);
  491.             if (!progress)
  492.                 return AFUNC_BADGLOBAL;
  493.  
  494.             len = strlen (local->buf);
  495.             if (local->tmpLen <= len) {
  496.                 local->overflow = 1;
  497.                 return AFUNC_OK;
  498.             }
  499.  
  500.             for (i = 0; i < len; i++) {
  501.                 local->tmpBuf[i] = local->buf[len - i - 1];
  502.                 (*progress) ();
  503.             }
  504.             local->tmpBuf[len] = 0;
  505.  
  506.             strcpy (local->buf, local->tmpBuf);
  507.             return AFUNC_OK;
  508.         }
  509.  
  510.     3.2.3    Capitalization Operation
  511.  
  512.     This just passes through the string and converts each letter to
  513.     uppercase, calling the progress function as it goes.
  514.  
  515.     (16)  Capitalize function body
  516.  
  517.         {
  518.             StringProgress          *progress;
  519.             char                    *c;
  520.  
  521.             if (version != 1)
  522.                 return AFUNC_BADVERSION;
  523.  
  524.             progress = (*global) ("Progress Function",
  525.                GFUSE_TRANSIENT);
  526.             if (!progress)
  527.                 return AFUNC_BADGLOBAL;
  528.  
  529.             for (c = local->buf; *c; c++) {
  530.                 (*progress) ();
  531.                 if (*c >= 'a' && *c <= 'z')
  532.                     *c = *c - 'a' + 'A';
  533.             }
  534.             return AFUNC_OK;
  535.         }
  536.  
  537.     3.2.4    Double Operation
  538.  
  539.     This doubles each character in the string by copying the buffer to
  540.     the temp buffer and moving twice as many characters back into the
  541.     buffer from there.  This will fail if the buffers are not big
  542.     enough.
  543.  
  544.     (17)  Double function body
  545.  
  546.         {
  547.             StringProgress          *progress;
  548.             int                      len, i;
  549.  
  550.             if (version != 1)
  551.                 return AFUNC_BADVERSION;
  552.  
  553.             progress = (*global) ("Progress Function",
  554.                GFUSE_TRANSIENT);
  555.             if (!progress)
  556.                 return AFUNC_BADGLOBAL;
  557.  
  558.             len = strlen (local->buf);
  559.             if (local->tmpLen < len || local->len - 1 < len * 2) {
  560.                 local->overflow = 1;
  561.                 return AFUNC_OK;
  562.             }
  563.  
  564.             strcpy (local->tmpBuf, local->buf);
  565.             for (i = 0; i < len; i++) {
  566.                 local->buf[i * 2]     = local->tmpBuf[i];
  567.                 local->buf[i * 2 + 1] = local->tmpBuf[i];
  568.                 (*progress) ();
  569.             }
  570.             local->buf[len * 2] = 0;
  571.  
  572.             return AFUNC_OK;
  573.         }
  574.  
  575.  
  576. 3.3    Implementing Servers
  577.  
  578. A plug-in module is really a wrapper around the activation function,
  579. and can be implemented several ways.  They can be single-service
  580. plug-ins, multiple-service plug-ins, or built-in.  This test includes
  581. one of each.
  582.  
  583.     3.3.1    Single-Service Plug-in -- Reverse
  584.  
  585.     The reverse operation is implemented as a single-service plug-in,
  586.     so there is one global class and server name.  The activation
  587.     function is called `Activate' (which it must be).
  588.  
  589.     The C program module itself includes the headers for the test
  590.     system and server-side plug-ins.  The source file is `tp_rev.c'.
  591.  
  592.     (18)  Test Reverse plug-in program
  593.  
  594.         #include <splug.h>
  595.         #include "t_plug.h"
  596.         #include <string.h>
  597.  
  598.         char            ServerClass[] = "StringXfrm";
  599.         char            ServerName[]  = "REVERSE";
  600.  
  601.             XCALL_(int)
  602.         Activate (<String transform arguments>)
  603.         {
  604.             XCALL_INIT;
  605.             <Reverse function body>
  606.         }
  607.  
  608.     3.3.2    Multiple-Service Plug-in -- Caps & Double
  609.  
  610.     The capitalize and double operations are implemented as one
  611.     multiple-service plug-in with two servers.  The activation
  612.     function entry points can have any name and are not exported
  613.     symbols.  They are associated with their server name in the
  614.     exported array of ServerRecords which has the required name
  615.     `ServerDesc'.  The source file for this is `tp_cpdb.c'.
  616.  
  617.     (19)  Test Caps and Double plug-in program
  618.  
  619.         #include <splug.h>
  620.         #include "t_plug.h"
  621.         #include <string.h>
  622.  
  623.             static XCALL_(int)
  624.         Capitalize (<String transform arguments>)
  625.         {
  626.             XCALL_INIT;
  627.             <Capitalize function body>
  628.         }
  629.  
  630.             static XCALL_(int)
  631.         Double (<String transform arguments>)
  632.         {
  633.             XCALL_INIT;
  634.             <Double function body>
  635.         }
  636.  
  637.         const char              class[] = "StringXfrm";
  638.         ServerRecord            ServerDesc[] = {
  639.             { class, "CAPS",        Capitalize },
  640.             { class, "DOUBLE",      Double },
  641.             { NULL }
  642.         };
  643.  
  644.     3.3.3    Built-in Server -- Length
  645.  
  646.     The length operation will be implemented as a built-in.  As
  647.     result, all we need is a local function entry point of any name in
  648.     the host program.
  649.  
  650.     (20)  Test host utilities
  651.  
  652.             static int
  653.         ActLength (<String transform arguments>)
  654.         {
  655.             <Length function body>
  656.         }
  657.         . . .
  658.  
  659.  
  660. 4    Creating a Plug-in
  661.  
  662. Methods for creating plug-ins have been developed for each of the
  663. target platforms.  Versions of a plug-in can be created for the
  664. different systems from a single source code with different linking
  665. instructions.  Each case that follows includes an implicit makefile
  666. rule to create a ".p" plug-in module from an object file.  The macro
  667. SLIB stands for the directory where the startup code and server
  668. libraries are located.  SINC is the include directory and OTHER_LIBS
  669. would be any other libraries need by the module.
  670.  
  671. The final section shows how to add your plug-in to the LightWave host.
  672.  
  673.  
  674. 4.1    Amiga -- SAS/C Compiler
  675.  
  676. Linking under SAS/C on the Amiga requires replacing the normal startup
  677. code with plug-in startup code, `serv_s.o'.  This can be done by using
  678. the "startup" option when using "sc link" or by placing `serv_s.o'
  679. first in the "FROM" list when using slink.  Modules must also be
  680. linked with the server library.  Object modules should be built
  681. without stack checking.
  682.  
  683.     (21)  Makefile examples
  684.  
  685.     .o.p:
  686.         sc link $(CFLAGS) startup=$(SLIB)serv_s.o $*.o\
  687.          $(SLIB)server.lib $(OTHER_LIBS) pname=$@
  688.     . . .
  689.  
  690.  
  691. 4.2    Amiga -- Manx Compiler
  692.  
  693. Linking under the Manx compiler on the Amiga requires using the
  694. plug-in startup code `serv_m.o', which must be placed first in the
  695. list of objects passed to ln.  The linker will warn about ".begin" and
  696. "_geta4" overriding library symbols, which is correct behavior in this
  697. case.  They should also be linked with the "server_m" library to get
  698. the Manx server library.
  699.  
  700.     (22)  Makefile examples
  701.     . . .
  702.     .o.p:
  703.         ln -o $@ $(SLIB)serv_m.o $*.o -lserver_m $(OTHER_LIBS)
  704.     . . .
  705.  
  706.  
  707. 4.3    Microsoft's Windows
  708.  
  709. Plug-in modules under Windows are just DLLs created by linking with
  710. `serv_w.obj' and `server.lib'.  There is no need to create a ".lib" or
  711. ".exp" file for the DLL, but the ".def" file should contain an export
  712. statement for the function `_InitServer'.  A usable default def file
  713. is provided as "serv.def" in the main include directory.  There is no
  714. DLL entry point function.
  715.  
  716.     (23)  Makefile examples
  717.     . . .
  718.     .obj.p:
  719.         link32 -dll -out:$@ -def:$(SINC)serv.def $*.obj\
  720.          $(SLIB)serv_w.obj server.lib $(OTHER_LIBS)
  721.     . . .
  722.  
  723.  
  724. 4.4    SGI Unix
  725.  
  726. Plug-in modules under IRIX are shared object modules linked with
  727. `serv_u.o' and `libserver.lib'.  The DSO should export the
  728. "_mod_descrip" symbol and use "serv_u" as startup code.
  729.  
  730.     (24)  Makefile examples
  731.     . . .
  732.     .o.p:
  733.         ld -shared -exported_symbol _mod_descrip -L$(SLIB)\
  734.          $(SLIB)serv_u.o $*.o -o $@ -lserver $(OTHER_LIBS)
  735.  
  736.  
  737. 4.5    Linking with LightWave
  738.  
  739. LightWave and Modeler read the names of servers from their startup
  740. configuration files.  This method is much faster than scanning a
  741. directory path and allows for some user customization of plug-in names
  742. (such as national localization).  It does require that the config file
  743. be accurate, since the host will blindly attempt to use servers that
  744. may not exist.  This is non-fatal but may be disconcerting to the
  745. user.
  746.  
  747. For Modeler, for example, the config file on the Amiga is
  748. "MOD-config"; on the SGI is ".lwmrc" and on Windows is "LWM.CFG".
  749. This file can contain any number of lines of the following form:
  750.  
  751.     (25)  Config file examples
  752.  
  753.     Plugin <class> <name> <module> <user name>
  754.     . . .
  755.  
  756. Each line describes a single server given by Class and Name.  The
  757. module is the plug-in file containing the server and the user name is
  758. the string to display on the interface for describing the server's
  759. function.  Class, name and module are delimited by spaces, and the
  760. user name is the rest of the line.  Here are some examples (lines wrap
  761. for readability -- each statement has to be a single line).
  762.  
  763.     (26)  Config file examples
  764.     . . .
  765.     Plugin CommandSequence Demo_AllBGLayers layerset.p Include Background
  766.     Plugin CommandSequence Demo_NextEmptyLayer layerset.p Next Empty
  767.     Plugin MeshDataEdit Demo_MakeSpikey z:lw/plugin/spikey.p Spikey
  768.        Subdivide
  769.     Plugin ImageLoader PDQ_Targa pdq/targa.lwp Truevision Targa Image
  770.  
  771. ====================================
  772.  
  773. LightWave Images -- Stuart Ferguson 12/5/94
  774.  
  775. 1  Introduction
  776.    (1)  Public declarations
  777.    (2)  Public foreward definitions
  778.  
  779. 2  Image I/O Server Interface
  780.     2.1  Image Loaders
  781.        (3)  Public types
  782.     2.2  Image Savers
  783.        (4)  Public types
  784.     2.3  Result Value
  785.        (5)  Public declarations
  786.     2.4  Image Transfer Protocols
  787.     2.4.1  Color Protocol
  788.        (6)  Public types
  789.     2.4.2  Index Protocol
  790.        (7)  Public types
  791.     2.4.3  Generic Protocol
  792.        (8)  Public types
  793.        (9)  Public foreward definitions
  794.     2.4.4  Error Handling
  795.     2.4.5  Misc Types
  796.        (10)  Public declarations
  797.        (11)  Public declarations
  798.     2.5  Monitor Objects
  799.        (12)  Monitor types
  800.        (13)  Monitor declarations
  801.  
  802. 3  Test Server
  803.     3.1  Targa Reader
  804.        (14)  Targa types
  805.        (15)  Targa functions
  806.        (16)  Targa utilities
  807.        (17)  Read targa data
  808.        (18)  Read targa lines
  809.        (19)  Read uncompressed targa line
  810.        (20)  Read compressed targa line
  811.        (21)  Read a targa pixel element into `bgra'
  812.        (22)  Store `bgra' pixel to line buffers
  813.     3.2  Targa Saver
  814.        (23)  Targa types
  815.        (24)  Targa functions
  816.        (25)  Targa utilities
  817.        (26)  Targa utilities
  818.        (27)  Targa utilities
  819.     3.3  Plugin Module
  820.        (28)  Targa Image server
  821.  
  822.  
  823. 1    Introduction
  824.  
  825. This module provides interfaces for dealing with image types commonly
  826. employed by LightWave users.  This allows the loading and saving of
  827. large, deep images in an expandable set of formats, and for accessing
  828. the data in a uniform manner regardless of the underlying data format.
  829. This interface is designed with plug-in image loaders and savers in
  830. mind and it provides some built-in IFF format support.
  831.  
  832. Image types are given by the following values.  RGB24 is an image with
  833. eight bits each of red, green and blue data for each pixel.  GREY8 is
  834. an image with eight bits of greyscale value at each pixel.  INDEX8 is
  835. an image with up to eight bits of color index at each pixel, mapped
  836. through a 24 bit color table.
  837.  
  838.     (1)  Public declarations
  839.  
  840.     #define IMG_RGB24       0
  841.     #define IMG_GREY8       1
  842.     #define IMG_INDEX8      2
  843.     . . .
  844.  
  845. Image color component, grey or index values are all unsigned chars
  846. scaled from 0 to 255.
  847.  
  848.     (2)  Public foreward definitions
  849.  
  850.     typedef unsigned char            ImageValue;
  851.     . . .
  852.  
  853.  
  854. 2    Image I/O Server Interface
  855.  
  856. The image input and output interfaces are designed to be extended with
  857. plug-in loaders and savers.  As result, each interface really only
  858. defines the local data structure for the activation function.
  859.  
  860.  
  861. 2.1    Image Loaders
  862.  
  863. Image loaders are servers that are called sequentially until one is
  864. able to load the image file.  An application will normally have a
  865. standard format in which images are saved, so that will normally be
  866. tried first after which other loaders may be tried in any order the
  867. host can determine.  If loaders are just scanned in the host plug-in
  868. database they will be called in something like alphabetical order.
  869.  
  870. The activation call for a loader gets passed a pointer to a filename
  871. as well as callbacks for image data transfer.  If the loader cannot
  872. open the file it sets the `result' field to IPSTAT_BADFILE and
  873. returns.  If it does not recognize the file format, it sets the result
  874. to IPSTAT_NOREC.  If it can load the image, it calls the `begin'
  875. callback with type of image protocol it would like.  The loader then
  876. sends the data from the file to the host through the protocol and
  877. calls the `done' callback when complete to allow the source to dispose
  878. of the protocol.  These callbacks are called with the `priv_data'
  879. pointer as the first field.
  880.  
  881.     (3)  Public types
  882.  
  883.     typedef struct st_ImLoaderLocal {
  884.         void             *priv_data;
  885.         int               result;
  886.         const char       *filename;
  887.         Monitor          *monitor;
  888.         ImageProtocolID (*begin) (void *, int type);
  889.         void            (*done) (void *, ImageProtocolID);
  890.     } ImLoaderLocal;
  891.     . . .
  892.  
  893.  
  894. 2.2    Image Savers
  895.  
  896. Image savers are servers of "ImageSaver" class that write an image out
  897. to a file in a single specific format.  The save format is typically
  898. chosen directly by the user with an interface showing the user names
  899. for the servers, so no scanning or ordering is required.
  900.  
  901. The activation call for savers gets a filename, a requested protocol
  902. type, and a callback for the host to output its image data to the
  903. saver protocol.  The flag in the `sendData' callback can contain the
  904. IMGF_ALPHA bit if the saver can store alpha data and IMGF_REVERSE bit
  905. if the saver wants the data sent bottom to top rather than top to
  906. bottom.  The saver should create a protocol and set flags most
  907. appropriate for the destination file format.  The `sendData' callback
  908. will return a non-zero error code if anything failed on the sending
  909. end or if the destination reports an error.
  910.  
  911.     (4)  Public types
  912.     . . .
  913.     typedef struct st_ImSaverLocal {
  914.         void            *priv_data;
  915.         int              result;
  916.         int              type;
  917.         const char      *filename;
  918.         Monitor         *monitor;
  919.         int            (*sendData) (void *, ImageProtocolID, int);
  920.     } ImSaverLocal;
  921.     . . .
  922.  
  923.  
  924. 2.3    Result Value
  925.  
  926. The result value indicates the status of the loader or saver upon
  927. completion.  If the load or save was sucessful, the value should be
  928. IPSTAT_OK.  If a loader fails to recognize a file as something it can
  929. load it should set the result to IPSTAT_NOREC.  If the server could
  930. not open the file it should return IPSTAT_BADFILE.  Any other error is
  931. just a generic failure of the loader or saver and so should set the
  932. result to IPSTAT_FAILED.  Other failure modes might be possible if
  933. required in the future.
  934.  
  935.     (5)  Public declarations
  936.     . . .
  937.     #define IPSTAT_OK                0
  938.     #define IPSTAT_NOREC             1
  939.     #define IPSTAT_BADFILE           2
  940.     #define IPSTAT_ABORT             3
  941.     #define IPSTAT_FAILED           99
  942.     . . .
  943.  
  944.  
  945. 2.4    Image Transfer Protocols
  946.  
  947. Images are passed from source to destination using an image protocol.
  948. Typically, the source will select the protocol type and the
  949. destination will create a protocol of that type.  The source will then
  950. send the image data to the source by calling callbacks in the
  951. protocol.  Both ends are then given an opportunity to clean up.  This
  952. is called a pusher protocol since the source "pushes" the data at the
  953. destination rather than the destination pulling it.
  954.  
  955. There are two protocols for the three types of images: color and index
  956. protocols.  The protocol `type' can have any of the same values as
  957. image type and determines the callbacks in the protocol and what they
  958. do.  Protocols contain a private data pointer which should be passed
  959. as the first argument to all the callbacks.
  960.  
  961.     2.4.1    Color Protocol
  962.  
  963.     The color protocol is used for the RGB and grey valued images
  964.     (RGB24 and GREY8 types).  The source starts the output by calling
  965.     the `setSize' function with the width and height of the image and
  966.     flags.  The flags can contain the IMGF_ALPHA bit to indicate that
  967.     the source data contains an alpha channel.  The source then sends
  968.     the data by calling the `sendLine' function with each image row
  969.     number and a pointer to a line of image data and a line of alpha
  970.     data, if any was indicated.  For greyscale images, the image line
  971.     consists of one image value per column in the image (G1 G2 ...
  972.     Gw).  For RGB images, this line data consists of three image
  973.     values per column of the image in RGB order (R1 G1 B1 R2 G2 B2 ...
  974.     Rw Gw Bw).  The alpha data is in greyscale format.
  975.  
  976.     (6)  Public types
  977.         . . .
  978.         typedef struct st_ColorProtocol {
  979.             int              type;
  980.             void            *priv_data;
  981.             void           (*setSize) (void *, int, int, int);
  982.             int            (*sendLine) (void *, int, const
  983.                ImageValue *,
  984.                         const ImageValue *);
  985.             int            (*done) (void *, int);
  986.         } ColorProtocol;
  987.         . . .
  988.  
  989.     2.4.2    Index Protocol
  990.  
  991.     Colormap index images use the index protocol.  The source must
  992.     first call `setSize' and `numColors' with image size, flags and
  993.     number of entries in the colormap.  The source must then set the
  994.     colormap by calling the `setMap' callback for each entry in the
  995.     colormap.  Any entry which is not set is left undefined.  The data
  996.     in the image is then filled in using the `sendLine' function just
  997.     like the greyscale case except that the image values are not grey
  998.     values but colormap indices.  Alpha values are in greyscale data
  999.     format.
  1000.  
  1001.     (7)  Public types
  1002.         . . .
  1003.         typedef struct st_IndexProtocol {
  1004.             int              type;
  1005.             void            *priv_data;
  1006.             void           (*setSize) (void *, int, int, int);
  1007.             void           (*numColors) (void *, int);
  1008.             void           (*setMap) (void *, int, ImageValue[3]);
  1009.             int            (*sendLine) (void *, int, const
  1010.                ImageValue *,
  1011.                         const ImageValue *);
  1012.             int            (*done) (void *, int);
  1013.         } IndexProtocol;
  1014.         . . .
  1015.  
  1016.     2.4.3    Generic Protocol
  1017.  
  1018.     The generic protocol is either of these possibilities plus the
  1019.     type field for easy type identifcation.
  1020.  
  1021.     (8)  Public types
  1022.         . . .
  1023.         typedef union un_ImageProtocol {
  1024.             int              type;
  1025.             ColorProtocol    color;
  1026.             IndexProtocol    index;
  1027.         } ImageProtocol;
  1028.  
  1029.     (9)  Public foreward definitions
  1030.         . . .
  1031.         typedef union un_ImageProtocol  *ImageProtocolID;
  1032.  
  1033.     2.4.4    Error Handling
  1034.  
  1035.     There are two specific mechanisms for dealing with errors that
  1036.     occur while using image protocols.  The destination can return
  1037.     error codes from the `sendLine' and `done' callbacks, and the
  1038.     source can pass an error code to the destination's `done'
  1039.     callback.
  1040.  
  1041.     If an error occurs in the source of a protocol, such as a failure
  1042.     partway though reading a file, the source can stop calling
  1043.     `sendLine' prematurely.  This will often trigger an error in the
  1044.     destination since it will have been keeping track of the amount of
  1045.     data sent.  The source should then also pass a non-zero error code
  1046.     to the `done' callback which will signal an error to the
  1047.     destination.
  1048.  
  1049.     If an error occurs in the destination of a protocol, such as a
  1050.     failure partway through saving an image, the destination should
  1051.     start to return a non-zero error code from `sendLine.' A
  1052.     well-written source will stop sending data when this happens, but
  1053.     the destination should be prepared to continue to get lines of
  1054.     data and to continue to return an error code.  A failed
  1055.     destination should also return a non-zero error code from the
  1056.     `done' callback.
  1057.  
  1058.     2.4.5    Misc Types
  1059.  
  1060.     Flags to be passed to `setSize' and `sendData' callbacks.
  1061.  
  1062.     (10)  Public declarations
  1063.         . . .
  1064.         #define IMGF_ALPHA               1
  1065.         #define IMGF_REVERSE             2
  1066.         . . .
  1067.  
  1068.     There are also some protocol macros defined to get the whole
  1069.     calling interface right.
  1070.  
  1071.     (11)  Public declarations
  1072.         . . .
  1073.         #define IP_SETSIZE(p,w,h,f)     (*(p)->setSize)
  1074.            ((p)->priv_data,w,h,f)
  1075.         #define IP_NUMCOLORS(p,n)       (*(p)->numColors)
  1076.            ((p)->priv_data,n)
  1077.         #define IP_SETMAP(p,i,val)      (*(p)->setMap)
  1078.            ((p)->priv_data,i,val)
  1079.         #define IP_SENDLINE(p,ln,d,a)   (*(p)->sendLine)
  1080.            ((p)->priv_data,ln,d,a)
  1081.         #define IP_DONE(p,err)          (*(p)->done)
  1082.            ((p)->priv_data,err)
  1083.  
  1084.  
  1085. 2.5    Monitor Objects
  1086.  
  1087. Monitors are simple data structures defining an interface which the
  1088. server can use to give feedback to the host on its progress in
  1089. performing some task.  They are introduced here for file loading and
  1090. saving feedback, but are used in a more general context so they are a
  1091. good thing to be familiar with.
  1092.  
  1093. A Monitor consists of some generic data and three functions: init,
  1094. step and done.  The `init' function is called first with the number of
  1095. steps in the process to be monitored, which is computed and passed up
  1096. from the server being monitored.  As the task is processed, the `step'
  1097. function is called with the number of steps just completed (often
  1098. one).  These step increments should eventually add up to the total
  1099. number and then the `done' function is called, but `done' may be
  1100. called early if there was a problem or the process was aborted.  The
  1101. `step' function will return one if the user requested an abort and
  1102. zero otherwise.
  1103.  
  1104.     (12)  Monitor types
  1105.  
  1106.     typedef struct st_Monitor {
  1107.         void             *data;
  1108.         void            (*init) (void *, unsigned int);
  1109.         int             (*step) (void *, unsigned int);
  1110.         void            (*done) (void *);
  1111.     } Monitor;
  1112.  
  1113. The server is masked from any errors in the monitor that may occur on
  1114. the host side of the interface.  If there is a problem with putting up
  1115. a monitor, the functions should still return normally, since the
  1116. monitor is for user feedback and is not that critical.
  1117.  
  1118. There are some macros provided to call a monitor which will do the
  1119. right thing if the monitor pointer is null, which means no monitoring
  1120. is necessary.  MON_INCR is used for step sizes greater than one and
  1121. MON_STEP is used for step sizes exactly one.
  1122.  
  1123.     (13)  Monitor declarations
  1124.  
  1125.     #define MON_INIT(mon,count)     if (mon) (*mon->init) (mon->data,
  1126.        count)
  1127.     #define MON_INCR(mon,d)         (mon ? (*mon->step) (mon->data, d) :
  1128.        0)
  1129.     #define MON_STEP(mon)           MON_INCR (mon, 1)
  1130.     #define MON_DONE(mon)           if (mon) (*mon->done) (mon->data)
  1131.  
  1132.  
  1133. 3    Test Server
  1134.  
  1135. This is a very simple server designed to test an alternate image
  1136. format.  The single plugin will load and save Targa 32 and 24 bit
  1137. formats.
  1138.  
  1139.  
  1140. 3.1    Targa Reader
  1141.  
  1142. The targa loader will recognize a targa file by reading the header
  1143. into this data struct.  The `type' gives the compression format and
  1144. interpretation of image data, `bits' gives the pixel size and
  1145. `reverse' indicates if the lines will come bottom to top.
  1146.  
  1147.     (14)  Targa types
  1148.  
  1149.     typedef struct st_TargaInfo {
  1150.         unsigned char    type, bits;
  1151.         short            width, height;
  1152.         int              reverse;
  1153.     } TargaInfo;
  1154.  
  1155.     #define CKPT_TGA_BADFILE        991
  1156.     #define CKPT_TGA_NOREC          992
  1157.     . . .
  1158.  
  1159. The main reader just reads the header, and if this can be matched as a
  1160. targa image it reads the body.  Errors will be captured by the
  1161. exception context and will set the result code.
  1162.  
  1163.     (15)  Targa functions
  1164.  
  1165.         int
  1166.     TargaLoader (
  1167.         long                     version,
  1168.         GlobalFunc              *global,
  1169.         ImLoaderLocal           *local,
  1170.         void                    *servData)
  1171.     {
  1172.         ReadStrmID               strm;
  1173.         TargaInfo                tga;
  1174.         int                      fail;
  1175.  
  1176.         if (version != 1)
  1177.             return AFUNC_BADVERSION;
  1178.  
  1179.         if (!CkptCapture (fail)) {
  1180.             if (fail == CKPT_ABORT)
  1181.                 local->result = IPSTAT_ABORT;
  1182.             else if (fail == CKPT_TGA_BADFILE)
  1183.                 local->result = IPSTAT_BADFILE;
  1184.             else if (fail == CKPT_TGA_NOREC)
  1185.                 local->result = IPSTAT_NOREC;
  1186.             else
  1187.                 local->result = IPSTAT_FAILED;
  1188.  
  1189.             return AFUNC_OK;
  1190.         }
  1191.  
  1192.         strm = StrmReadOpen (local->filename, NULL);
  1193.         if (!strm)
  1194.             CkptRecover (CKPT_TGA_BADFILE);
  1195.         ARM1 (StrmReadClose, strm);
  1196.  
  1197.         if (!ReadTargaHeader (strm, &tga))
  1198.             CkptRecover (CKPT_TGA_NOREC);
  1199.  
  1200.         <Read targa data>
  1201.  
  1202.         StrmReadClose (strm);
  1203.         CkptEnd ();
  1204.  
  1205.         local->result = IPSTAT_OK;
  1206.         return AFUNC_OK;
  1207.     }
  1208.     . . .
  1209.  
  1210. The reader will check just a very few things in the header before it
  1211. decides it can load the file.  This might be a problem since targa
  1212. files are much less self-identifying than others.  Any values that are
  1213. out of range cause this to return 0, indicating failure to recognize.
  1214. If it returns 1, the info has been read.
  1215.  
  1216.     (16)  Targa utilities
  1217.  
  1218.         static int
  1219.     ReadTargaHeader (
  1220.         ReadStrmID               strm,
  1221.         TargaInfo               *tga)
  1222.     {
  1223.         unsigned char            byte, idLen;
  1224.  
  1225.         (*strm->readBytes) (strm, &idLen, 1);
  1226.         (*strm->readBytes) (strm, &byte, 1);
  1227.         if (byte)
  1228.             return 0;
  1229.  
  1230.         (*strm->readBytes) (strm, &tga->type, 1);
  1231.         if (tga->type != 2 && tga->type != 10)
  1232.             return 0;
  1233.  
  1234.         (*strm->skipBytes) (strm, 9);
  1235.         (*strm->readIWords) (strm, &tga->width, 1);
  1236.         (*strm->readIWords) (strm, &tga->height, 1);
  1237.  
  1238.         (*strm->readBytes) (strm, &tga->bits, 1);
  1239.         if (tga->bits != 24 && tga->bits != 32)
  1240.             return 0;
  1241.  
  1242.         (*strm->readBytes) (strm, &byte, 1);
  1243.         byte &= 0xF0;
  1244.         if (byte == 0)
  1245.             tga->reverse = 1;
  1246.         else if (byte == 0x20)
  1247.             tga->reverse = 0;
  1248.         else
  1249.             return 0;
  1250.  
  1251.         (*strm->skipBytes) (strm, idLen);
  1252.         return 1;
  1253.     }
  1254.     . . .
  1255.  
  1256. We will always send the data in RGB24 format, since we currently only
  1257. recognize targa 32 and 24 bit formats.  Buffers are allocated to
  1258. transfer the rgb and alpha data in the right byte-packing order.  The
  1259. protocol is started and recovery actions are armed in case we fail
  1260. partway through.
  1261.  
  1262.     (17)  Read targa data
  1263.  
  1264.     {
  1265.         ImageProtocolID          ip;
  1266.         ColorProtocol           *cp;
  1267.         ImageValue              *buf, *abuf;
  1268.         int                      bufSize, i, alpha;
  1269.  
  1270.         ip = (*local->begin) (local->priv_data, IMG_RGB24);
  1271.         if (!ip)
  1272.             CkptRecover (CKPT_IO_ERROR);
  1273.  
  1274.         alpha = (tga.bits == 32);
  1275.  
  1276.         bufSize = tga.width * 4;
  1277.         buf = NEW_Z (bufSize);
  1278.         ARM_Z (buf, bufSize);
  1279.         abuf = (alpha ? buf + 3 * tga.width : NULL);
  1280.  
  1281.         cp = &ip->color;
  1282.         IP_SETSIZE (cp, tga.width, tga.height, (alpha ? IMGF_ALPHA
  1283.            : 0));
  1284.  
  1285.         ARM2 (local->done, local->priv_data, cp);
  1286.  
  1287.         MON_INIT (local->monitor, tga.height);
  1288.         if (local->monitor)
  1289.             ARM1 (local->monitor->done, local->monitor->data);
  1290.  
  1291.         if (CkptBegin ()) {
  1292.             ARM2 ((void*)cp->done, cp->priv_data, -1);
  1293.             <Read targa lines>
  1294.             CkptEnd ();
  1295.         }
  1296.         if (IP_DONE (cp, 0))
  1297.             CkptRecover (CKPT_IO_ERROR);
  1298.  
  1299.         MON_DONE (local->monitor);
  1300.         (*local->done) (local->priv_data, ip);
  1301.         FREE_Z (buf, bufSize);
  1302.     }
  1303.  
  1304. Basically we just read all the lines in forward or reverse order.
  1305. They may be compressed or not.
  1306.  
  1307.     (18)  Read targa lines
  1308.  
  1309.     for (i = 0; i < tga.height; i++) {
  1310.         int                      ln, x;
  1311.         unsigned char            bgra[4];
  1312.         ImageValue              *rgbBuf, *alphaBuf;
  1313.  
  1314.         rgbBuf = buf;
  1315.         alphaBuf = abuf;
  1316.         if (tga.type == 2) {
  1317.             <Read uncompressed targa line>
  1318.         } else {
  1319.             <Read compressed targa line>
  1320.         }
  1321.  
  1322.         ln = (tga.reverse ? tga.height - i - 1: i);
  1323.         if (IP_SENDLINE (cp, ln, buf, abuf))
  1324.             break;
  1325.  
  1326.         if (MON_STEP (local->monitor))
  1327.             CkptRecover (CKPT_ABORT);
  1328.     }
  1329.  
  1330. Uncompressed lines of data are just `width' pixels which we read
  1331. sequentially.
  1332.  
  1333.     (19)  Read uncompressed targa line
  1334.  
  1335.     for (x = 0; x < tga.width; x++) {
  1336.         <Read a targa pixel element into `bgra'>
  1337.         <Store `bgra' pixel to line buffers>
  1338.     }
  1339.  
  1340. A compressed line is enough pixels in literals and runs to fill a
  1341. scanline.  If the scanline is not exactly filled, this is an error.
  1342.  
  1343.     (20)  Read compressed targa line
  1344.  
  1345.     x = 0;
  1346.     while (x < tga.width) {
  1347.         unsigned char            test;
  1348.         int                      count, k;
  1349.  
  1350.         (*strm->readBytes) (strm, &test, 1);
  1351.         count = (test & 0x7F) + 1;
  1352.         if (test & 0x80) {
  1353.             <Read a targa pixel element into `bgra'>
  1354.             for (k = 0; k < count; k++) {
  1355.                 <Store `bgra' pixel to line buffers>
  1356.             }
  1357.         } else {
  1358.             for (k = 0; k < count; k++) {
  1359.                 <Read a targa pixel element into `bgra'>
  1360.                 <Store `bgra' pixel to line buffers>
  1361.             }
  1362.         }
  1363.         x += count;
  1364.     }
  1365.     if (x != tga.width)
  1366.         CkptRecover (CKPT_IO_ERROR);
  1367.  
  1368. 24 and 32 bit targa pixels are just 3 or 4 bytes in BGR(A) order.  We
  1369. read that into an array that will be unpacked into the format we want.
  1370.  
  1371.     (21)  Read a targa pixel element into `bgra'
  1372.  
  1373.     (*strm->readBytes) (strm, bgra, (alpha ? 4 : 3));
  1374.  
  1375. Once we have read a pixel we can store it to the accumulating output
  1376. row by sticking the rgb and optional alpha into their buffers.
  1377.  
  1378.     (22)  Store `bgra' pixel to line buffers
  1379.  
  1380.     *rgbBuf++ = bgra[2];
  1381.     *rgbBuf++ = bgra[1];
  1382.     *rgbBuf++ = bgra[0];
  1383.     if (alpha)
  1384.         *alphaBuf++ = bgra[3];
  1385.  
  1386.  
  1387. 3.2    Targa Saver
  1388.  
  1389.     (23)  Targa types
  1390.     . . .
  1391.     typedef struct st_TargaSave {
  1392.         WriteStrmID      strm;
  1393.         Monitor         *mon;
  1394.         int              width, height;
  1395.         int              alpha, result;
  1396.     } TargaSave;
  1397.  
  1398. The targa saver sets up a protocol of the RGB24 type and requests a
  1399. send from the source.  Since the protocol callbacks have to return
  1400. result codes, the ckpt mechanism is more of a hinderance here.
  1401.  
  1402.     (24)  Targa functions
  1403.     . . .
  1404.         int
  1405.     TargaSaver (
  1406.         long                     version,
  1407.         GlobalFunc              *global,
  1408.         ImSaverLocal            *local,
  1409.         void                    *servData)
  1410.     {
  1411.         ImageProtocol            prot;
  1412.         TargaSave                tga;
  1413.  
  1414.         if (version != 1)
  1415.             return AFUNC_BADVERSION;
  1416.  
  1417.         if (local->type != IMG_RGB24) {
  1418.             local->result = IPSTAT_FAILED;
  1419.             return AFUNC_OK;
  1420.         }
  1421.  
  1422.         tga.strm = StrmWriteOpen (local->filename);
  1423.         if (!tga.strm) {
  1424.             local->result = IPSTAT_BADFILE;
  1425.             return AFUNC_OK;
  1426.         }
  1427.  
  1428.         tga.result = IPSTAT_OK;
  1429.         tga.mon = local->monitor;
  1430.  
  1431.         prot.type = IMG_RGB24;
  1432.         prot.color.priv_data = &tga;
  1433.         prot.color.setSize = Targa_SetSize;
  1434.         prot.color.sendLine = Targa_SendLine;
  1435.         prot.color.done = Targa_Done;
  1436.  
  1437.         (*local->sendData) (local->priv_data, &prot, IMGF_ALPHA);
  1438.  
  1439.         StrmWriteClose (tga.strm);
  1440.         local->result = tga.result;
  1441.         return AFUNC_OK;
  1442.     }
  1443.  
  1444. The set size callback will just record the size and alpha status in
  1445. the save info and write the header.  The header is mostly zero expcpt
  1446. for a few bytes with special values and the size as reversed byte
  1447. order words.
  1448.  
  1449.     (25)  Targa utilities
  1450.     . . .
  1451.         XCALL_(static void)
  1452.     Targa_SetSize (
  1453.         TargaSave               *tga,
  1454.         int                      w,
  1455.         int                      h,
  1456.         int                      flags)
  1457.     {
  1458.         unsigned char            hdr[12];
  1459.         short                    size[2];
  1460.         int                      fail;
  1461.  
  1462.         if (!CkptCapture (fail)) {
  1463.             tga->result = IPSTAT_FAILED;
  1464.             return;
  1465.         }
  1466.  
  1467.         tga->width = w;
  1468.         tga->height = h;
  1469.         tga->alpha = ((flags & IMGF_ALPHA) != 0);
  1470.  
  1471.         memset (hdr, 0, 12);
  1472.         hdr[2] = 2;
  1473.         (*tga->strm->writeBytes) (tga->strm, hdr, 12);
  1474.  
  1475.         size[0] = w;
  1476.         size[1] = h;
  1477.         (*tga->strm->writeIWords) (tga->strm, size, 2);
  1478.  
  1479.         hdr[0] = (tga->alpha ? 32 : 24);
  1480.         hdr[1] = 0x20;
  1481.         (*tga->strm->writeBytes) (tga->strm, hdr, 2);
  1482.  
  1483.         if (tga->mon)
  1484.             MON_INIT (tga->mon, tga->height);
  1485.  
  1486.         CkptEnd ();
  1487.     }
  1488.     . . .
  1489.  
  1490. Writing a line is really easy.  The pixel loop just unwraps the rgb
  1491. and optional alpha data into targa pixel format and writes it.  Write
  1492. errors will return an error code, but nothing else.
  1493.  
  1494.     (26)  Targa utilities
  1495.     . . .
  1496.         static int
  1497.     Targa_SendLine (
  1498.         TargaSave               *tga,
  1499.         int                      line,
  1500.         const ImageValue        *data,
  1501.         const ImageValue        *adata)
  1502.     {
  1503.         unsigned char            bgra[4];
  1504.         int                      i, plen;
  1505.         int                      fail;
  1506.  
  1507.         if (tga->result != IPSTAT_OK)
  1508.             return -1;
  1509.  
  1510.         if (!CkptCapture (fail)) {
  1511.             if (fail == CKPT_ABORT)
  1512.                 tga->result = IPSTAT_ABORT;
  1513.             else
  1514.                 tga->result = IPSTAT_FAILED;
  1515.  
  1516.             return -1;
  1517.         }
  1518.  
  1519.         plen = (tga->alpha ? 4 : 3);
  1520.  
  1521.         for (i = 0; i < tga->width; i++) {
  1522.             bgra[2] = *data++;
  1523.             bgra[1] = *data++;
  1524.             bgra[0] = *data++;
  1525.             if (tga->alpha)
  1526.                 bgra[3] = *adata++;
  1527.  
  1528.             (*tga->strm->writeBytes) (tga->strm, bgra, plen);
  1529.         }
  1530.  
  1531.         if (tga->mon && MON_STEP (tga->mon))
  1532.             CkptRecover (CKPT_ABORT);
  1533.  
  1534.         CkptEnd ();
  1535.         return 0;
  1536.     }
  1537.     . . .
  1538.  
  1539. The `done' callback completes the monitor transaction and returns the
  1540. aggregate error status.
  1541.  
  1542.     (27)  Targa utilities
  1543.     . . .
  1544.         static int
  1545.     Targa_Done (
  1546.         TargaSave               *tga,
  1547.         int                      error)
  1548.     {
  1549.         if (error)
  1550.             tga->result = IPSTAT_FAILED;
  1551.  
  1552.         if (tga->mon)
  1553.             MON_DONE (tga->mon);
  1554.  
  1555.         return (tga->result != IPSTAT_OK);
  1556.     }
  1557.  
  1558.  
  1559. 3.3    Plugin Module
  1560.  
  1561.     (28)  Targa Image server
  1562.  
  1563.     #include <image.h>
  1564.     #include <strmu.h>
  1565.     #include <splug.h>
  1566.     #include <std.h>
  1567.  
  1568.     <Targa types>
  1569.     <Targa utilities>
  1570.     <Targa functions>
  1571.  
  1572.     ServerRecord            ServerDesc[] = {
  1573.         { "ImageSaver",   "Targa",      TargaSaver },
  1574.         { "ImageLoader",  "Targa",      TargaLoader },
  1575.         { NULL }
  1576.     };
  1577.